package com.kly.sc.api.application.service.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class RedisService {
    @Autowired
    private RedisTemplate redisTemplate;

    @PostConstruct
    public void customizeRedisTemplate() {
        RedisSerializer stringSerializer = new StringRedisSerializer();
        RedisSerializer valueSerializer = new GenericToStringSerializer<>(Object.class);

        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(valueSerializer);
        redisTemplate.setHashKeySerializer(valueSerializer);
        redisTemplate.setHashValueSerializer(valueSerializer);
        redisTemplate.afterPropertiesSet();
    }


    public String getString(String keyFormat, String... keyValues) {
        String format = format(keyFormat, keyValues);
        final Object o = redisTemplate.opsForValue().get(format);
        if (Objects.isNull(o)) {
            return null;
        }
        return o.toString().trim();
    }

    public long getNumber(String keyFormat, String... keyValues) {
        String format = format(keyFormat, keyValues);
        Object value = redisTemplate.opsForValue().get(format);
        if (value != null) {
            return Long.parseLong(value.toString());
        }
        return 0L;
    }

    public void setNumber(String keyFormat, long value, String... keyValues) {
        String format = format(keyFormat, keyValues);
        redisTemplate.opsForValue().set(format, value);
    }

    public void setNumber(String keyFormat, long value, long expireInSeconds, String... keyValues) {
        String format = format(keyFormat, keyValues);
        redisTemplate.opsForValue().set(format, value, expireInSeconds, TimeUnit.SECONDS);
    }

    public void setString(String keyFormat, String value, String... keyValues) {
        String format = format(keyFormat, keyValues);
        redisTemplate.opsForValue().set(format, value);
    }

    public void setString(String keyFormat, String value, long expireInSeconds, String... keyValues) {
        String format = format(keyFormat, keyValues);
        redisTemplate.opsForValue().set(format, value, expireInSeconds, TimeUnit.SECONDS);
    }


    // 设置过期时间
    public void setExpire(String keyFormat, long value, long expireInSeconds, String... keyValues) {
        String format = format(keyFormat, keyValues);
        redisTemplate.expire(format, expireInSeconds, TimeUnit.SECONDS);
    }

    // 查看过期时间
    public long getExpiration(String keyFormat, String... keyValues) {
        String format = format(keyFormat, keyValues);
        return redisTemplate.getExpire(format, TimeUnit.SECONDS);
    }

    // 对整数执行加法
    public long incrementNumber(String keyFormat, long delta, String... keyValues) {
        String format = format(keyFormat, keyValues);
        ValueOperations<String, Object> valueOps = redisTemplate.opsForValue();
        return valueOps.increment(format, delta);
    }

    // 对整数执行减法
    public long decrementNumber(String keyFormat, long delta, String... keyValues) {
        String format = format(keyFormat, keyValues);
        ValueOperations<String, Object> valueOps = redisTemplate.opsForValue();
        return valueOps.increment(format, -delta);
    }

    public void addDelayTask(String keyFormat, String task, long delayTimes, String... keyValues) {
        String format = format(keyFormat, keyValues);
        long score = System.currentTimeMillis() / 1000 + delayTimes;
        redisTemplate.opsForZSet().add(format, task, score);
    }

    public void addDelayTask(String keyFormat, Set<String> task, long delayTimes, String... keyValues) {
        String format = format(keyFormat, keyValues);
        long score = System.currentTimeMillis() / 1000 + delayTimes;
        Set<ZSetOperations.TypedTuple<String>> tuples = task.stream()
                .map(member -> new DefaultTypedTuple<>(member, Double.longBitsToDouble(score)))
                .map(tuple -> (ZSetOperations.TypedTuple<String>) tuple)
                .collect(Collectors.toSet());
        redisTemplate.opsForZSet().add(format, tuples);
    }

    /**
     * 从在线队列中获取元素，最多一百个
     */
    public Set<String> getDelayTask(String keyFormat, String... keyValues) {
        String format = format(keyFormat, keyValues);
        long score = System.currentTimeMillis() / 1000;
        Set set = redisTemplate.opsForZSet().rangeByScore(format, 0, score);

        return set;
    }

    /**
     * 删除延迟队列中过期消息
     * @return
     */
    public long delDelayTask(String keyFormat,Set<String> task, String... keyValues) {
        String format = format(keyFormat, keyValues);
        return redisTemplate.opsForZSet().remove(format, task.toArray());
    }

    /**
     * 当redis不存在插入的key的时候允许插入，否则插入失败
     */
    public Boolean setNX(String keyFormat, String value, long expireInSeconds, String... keyValues) {
        String format = format(keyFormat, keyValues);
        Boolean wasSet = redisTemplate.opsForValue().setIfAbsent(format, value);
        if (wasSet != null && wasSet) {
            redisTemplate.expire(format, expireInSeconds, TimeUnit.SECONDS);
        }
        return wasSet;
    }

    /**
     * 删除HashMap field字段
     *
     * @param keyFormat
     * @param field
     * @param keyValues
     */
    public long hDel(String keyFormat, String field, String... keyValues) {
        String key = format(keyFormat, keyValues);
       return redisTemplate.opsForHash().delete(key, field);
    }


    public void hDelAll(String keyFormat, String... keyValues) {
        String key = format(keyFormat, keyValues);
        redisTemplate.opsForHash().delete(key);
    }

    /**
     * 获取HashMap 所有值
     *
     * @param keyFormat
     * @param keyValues
     * @return
     */
    public Map<String, String> hGetAll(String keyFormat, String... keyValues) {
        String key = format(keyFormat, keyValues);
        Map<String, String> result = redisTemplate.opsForHash().entries(key);
        return result;
    }

    /**
     * 获取hash中指定key 的值
     *
     * @param keyFormat
     * @param hashKey
     * @param keyValues
     * @return
     */
    public String hGet(String keyFormat, String hashKey, String... keyValues) {
        String key = format(keyFormat, keyValues);
        Object o = redisTemplate.opsForHash().get(key, hashKey);
        return o.toString();
    }

    /**
     * @param keyFormat
     * @param hashKey
     * @param keyValues
     * @return
     */
    public void hPut(String keyFormat, String hashKey, String value, String... keyValues) {
        String key = format(keyFormat, keyValues);
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    public static String format(String formatKey, String... keyValues) {
        if (keyValues != null && keyValues.length != 0) {
            StringBuilder key = new StringBuilder();
            char[] chars = formatKey.toCharArray();
            int index = -1;
            boolean inmark = false;
            boolean firstinmark = false;

            for (int i = 0; i < chars.length; ++i) {
                char ch = chars[i];
                if (ch == '{') {
                    ++index;
                    inmark = true;
                    firstinmark = true;
                } else if (inmark) {
                    if (firstinmark) {
                        firstinmark = false;
                        key.append(keyValues[index]);
                    }

                    if (ch == '}') {
                        inmark = false;
                    }
                } else if (ch == '}') {
                    inmark = false;
                } else {
                    key.append(chars[i]);
                }
            }

            return key.toString();
        } else {
            return formatKey;
        }
    }

    public void del(String key) {
        Set<String> keys = redisTemplate.keys(key);
        if (!keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
    }

    public void del(String keyFormat, String... keyValues) {
        String format = format(keyFormat, keyValues);
        redisTemplate.delete(format);
    }
}





